package com.cloud.test.region.servera.route;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @classname: GrayLoadBalancerRule
 * @description: 灰度轮询路由策略
 * @author: lds
 * @date: 2020/12/16 11:31
 * @version: V1.0
 */
@Slf4j
public class GrayLoadBalancerRule extends AbstractLoadBalancerRule {

    private AtomicInteger nextServerCyclicCounter;

    @Autowired
    private GrayRuleProperties grayRuleProperties;

    public GrayLoadBalancerRule(){
        nextServerCyclicCounter = new AtomicInteger(0);
    }

    public GrayLoadBalancerRule(ILoadBalancer lb) {
        this();
        setLoadBalancer(lb);
    }

    @Override
    public Server choose(Object key) {
        return choose(getLoadBalancer(), key);
    }

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }

    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            log.warn("no load balancer");
            return null;
        }

        Server server = null;
        int count = 0;
        while (server == null && count++ < 10) {
            List<Server> reachableServers = lb.getReachableServers();
            List<Server> allServers = createAvailableServer(lb);
            int upCount = reachableServers.size();
            int serverCount = allServers.size();

            if ((upCount == 0) || (serverCount == 0)) {
                log.warn("No up servers available from load balancer: " + lb);
                return null;
            }

            int nextServerIndex = incrementAndGetModulo(serverCount);
            server = allServers.get(nextServerIndex);

            if (server == null) {
                /* Transient. */
                Thread.yield();
                continue;
            }

            if (server.isAlive() && (server.isReadyToServe())) {
                return (server);
            }

            // Next.
            server = null;
        }

        if (count >= 10) {
            log.warn("No available alive servers after 10 tries from load balancer: " + lb);
        }
        return server;
    }

    private int incrementAndGetModulo(int modulo) {
        for (;;) {
            int current = nextServerCyclicCounter.get();
            int next = (current + 1) % modulo;
            if (nextServerCyclicCounter.compareAndSet(current, next)){
                return next;
            }
        }
    }

    /**
     * 根据灰度标识，区分灰度应用与非灰度
     */
    List<Server> createAvailableServer(ILoadBalancer lb){
        List<Server> servers = lb.getAllServers();
        List<Server> availableServers = new ArrayList<>();
        if(null != servers && servers.size() > 0){
            for (Server server : servers) {
                if(grayRuleProperties.getZone().equals(server.getZone())){
                    // 单区域调用
                    availableServers.add(server);
                }
            }
        }
        if(grayRuleProperties.isTransZone() && availableServers.isEmpty()){
            // 允许跨区域调用，当前区域所有服务节点全部故障，可以跨区域调用其他节点
            return servers;
        }
        return availableServers;
    }

}
